﻿using System;
using System.IO;
using System.Windows;
#if SL4BETA
using System.Windows.Interop;
#else
using System.Windows.Interop;
using System.Runtime.InteropServices.Automation;
#endif
using System.Windows.Media.Imaging;

namespace Silverlight.Media.Encoder
{
    public class WindowsMediaEncoder
    {
        private const string CLR_PROXY_PROGID = "ClrProxy";
        private const string ENCODER_ASSEMBLY_NAME = "Desktop.Media.Encoder.dll";
        private const string DESKTOP_ENCODER_CLASS_NAME = "Desktop.Media.Encoder.WindowsMediaEncoder";
        private readonly string m_installationPath;
        private dynamic m_desktopEncoder;
        private string m_encodingProfileData;
        private string m_encodingProfileFilename;
        private bool m_networkStream;
        private string m_outputFilename;

        private int m_tcpPort;
        private Size m_videoSize;
        private int m_bitsPerPixel = 32;

        private short m_channels;
        private short m_bits;
        private int m_sampleRate;
        private bool m_audioInitialized;
        private bool m_videoInitialized;

        private long m_lastAudioSampleTime = 0;

        public WindowsMediaEncoder(string installationPath)
        {
            m_installationPath = installationPath;
        }

        public int TcpPort
        {
            get { return m_tcpPort; }
            set
            {
                m_tcpPort = value;
            }
        }

        public bool NetworkStream
        {
            get { return m_networkStream; }
            set
            {
                m_networkStream = value;
            }
        }

        public string OutputFilename
        {
            get { return m_outputFilename; }
            set
            {
                m_outputFilename = value;
            }
        }

        public string EncodingProfileFilename
        {
            get { return m_encodingProfileFilename; }
            set
            {
                m_encodingProfileFilename = value;
            }
        }

        public string EncodingProfileData
        {
            get { return m_encodingProfileData; }
            set
            {
                m_encodingProfileData = value;
            }
        }

        private void CreateDesktopEncoder()
        {
#if SL4BETA
            dynamic fullDotNetProxy = ComAutomationFactory.CreateObject(CLR_PROXY_PROGID);
#else
            dynamic fullDotNetProxy = AutomationFactory.CreateObject(CLR_PROXY_PROGID);
#endif
            string fullAssemblyPath = Path.Combine(m_installationPath, ENCODER_ASSEMBLY_NAME);
            dynamic assembly = fullDotNetProxy.GetAssembly(fullAssemblyPath);
            dynamic wmvFileType = fullDotNetProxy.GetDotNetType(assembly, DESKTOP_ENCODER_CLASS_NAME);
            m_desktopEncoder = fullDotNetProxy.CreateInstance(wmvFileType);
        }

        private void SetEncoderProperties()
        {
            if (m_desktopEncoder == null)
                return;

            m_desktopEncoder.TcpPort = TcpPort;
            m_desktopEncoder.NetworkStream = NetworkStream;
            m_desktopEncoder.OutputFilename = OutputFilename;
            m_desktopEncoder.EncodingProfileData = EncodingProfileData;
        }

        public void AppendVideoSample(WriteableBitmap bmp, long sampleTime)
        {
            if (m_desktopEncoder != null)
                m_desktopEncoder.AppendVideoSample(bmp.Pixels, sampleTime);
            else
                return;
        }

        public void AppendVideoSample(byte[] sample, long sampleTime)
        {
            if (m_desktopEncoder != null)
                m_desktopEncoder.AppendVideoSampleBuffer(sample, sampleTime);
            else
                return;
        }

        public void AppendAudioSample(byte[] sample, long sampleTime)
        {
            if (m_desktopEncoder != null)
                m_desktopEncoder.AppendAudioSample(sample, sampleTime);
            else
                return;

            m_lastAudioSampleTime = sampleTime;
        }

        public void InitializeVideo(int width, int height, int bitsPerPixel)
        {
            m_videoSize = new Size(width, height);
            m_bitsPerPixel = bitsPerPixel;
            m_videoInitialized = true;
        }

        public void InitializeAudio(short channels, short bits, int sampleRate)
        {
            m_channels = channels;
            m_bits = bits;
            m_sampleRate = sampleRate;
            m_audioInitialized = true;
        }

        public bool IsStarted
        {
            get; private set;
        }

        public void Start()
        {
            if (m_desktopEncoder != null)
                Stop();

            CreateDesktopEncoder();

            SetEncoderProperties();

            if(m_desktopEncoder != null)
            {
                if (m_videoInitialized)
                    m_desktopEncoder.InitializeVideo(m_videoSize.Width, m_videoSize.Height, m_bitsPerPixel);

                if (m_audioInitialized)
                    m_desktopEncoder.InitializeAudio(m_channels, m_bits, m_sampleRate);
            }
            else
                throw new Exception("Desktop encoder not initialized");

            m_desktopEncoder.BeginWriting();

            IsStarted = true;
        }

        public void Reset()
        {
            Stop();
            m_videoInitialized = false;
            m_audioInitialized = false;
        }

        public void Stop()
        {
            IsStarted = false;
            
            m_lastAudioSampleTime = 0;

            if (m_desktopEncoder == null)
                return;
            
            m_desktopEncoder.Close();

            m_desktopEncoder = null;
        }
    }
}